#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>

#include <syslog.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <pthread.h>
#include <time.h>
#include "TCPServer.h"
#include "Configuration.h"
#include "ServerProtocol.h"
#include "ConnectionManager.h"
#include <Log.h>
#include <boost/thread/thread.hpp>

#define SERVER_PORT 5000

DataBase* g_db = NULL;
static CSNetworkLib::TCPServer<ServerProtocol> * server = NULL;

void RunThread()
{
    try {
        while(!boost::this_thread::interruption_requested() && server!=NULL && server->acceptClient()) {}
    } catch (const std::exception &e) {
        ERR("THREAD EXCEPTION: %s", e.what());
    }
}

int32_t main(int32_t argc, char *argv[]) {
    Configuration cfg("", (((argc > 1) && (argv[1][0] != 0)) ? argv[1] : "config"));
    if (!cfg.isLoaded()) {
          printf("Configuration file: '%s' [.xml] not found\n", (((argc > 1) && (argv[1][0] != 0)) ? argv[1] : "config"));
          return 1;
    }

    std::string tmp = cfg.get<std::string>("config.log.file", "");
    if (tmp.empty())
    {
        initLog();
    }
    else
    {
        initLog(tmp.c_str(), false);
    }

    int ret = 0;
    do
    {
		FUNCTION_TRACK
        try {
            g_db = new DataBase(cfg.get<std::string > ("config.server.db.ip", "127.0.0.1"),
                    cfg.get<std::string > ("config.server.db.user", ""),
                    cfg.get<std::string > ("config.server.db.password", ""),
                    cfg.get<std::string > ("config.server.db.schema", "default"));

            if (NULL == g_db || !g_db->isConnected()) {
                ERR("Failed to connect to MySQL database (IP: '%s', User: '%s', Using password: %s, Schema: '%s')",
                        cfg.get<std::string > ("config.server.db.ip", "127.0.0.1").c_str(),
                        cfg.get<std::string > ("config.server.db.user", "").c_str(),
                        cfg.get<std::string > ("config.server.db.password", "").empty() ? "No" : "Yes",
                        cfg.get<std::string > ("config.server.db.schema", "default").c_str());
                if (g_db != NULL) {
                    delete g_db;
                }
                ret = 2;
                break;
            }
            g_db->SetStorageInterface(&ConnectionManager::getInstance());

            try {
                server = new CSNetworkLib::TCPServer<ServerProtocol > (cfg.get<uint16_t>("config.server.cport", SERVER_PORT));
            } catch (...) {
                delete g_db;
                ERR("Socket already in use, port %d is busy", cfg.get<uint16_t>("config.server.cport", SERVER_PORT));
                ret = 3;
                break;
            }
            INFO("Waiting for connections on port %d...", cfg.get<uint16_t>("config.server.cport", SERVER_PORT));
            printf("Press ENTER key to break...\n");
            boost::thread workerThread(RunThread);
            do
            {
                int c = getchar();
                if (13 == c || 10 == c) break;
                boost::this_thread::sleep(boost::posix_time::milliseconds(500));
            } while(true);
            workerThread.interrupt();
            server->Break();
            workerThread.join();
            boost::this_thread::sleep(boost::posix_time::milliseconds(1000));
        } catch (const std::exception &e) {
            ERR("MAIN EXCEPTION: %s", e.what());
        }
        delete server;
        server = NULL;
        delete g_db;
        g_db = NULL;
    } while(false);
    INFO("Application shutdown");
    shutdownLog();
    return ret;
}
